﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace ActiveDirectoryUtilities.CommonLibrary
{
    public class DistinguishedName
    {

        private string _dn;
        private string[] _components;
        private int _padLength = 0;
        static private string _delimiter = ",";

        public DistinguishedName(string sDN)
        {
            _dn = sDN.Trim().Trim('"');
            _components = DN2Components(_dn);
        }

        public override string ToString()
        {
            return _dn;
        }

        public int padLength
        {
            get { return _padLength; }
            set { _padLength = value; }
        }

        public string Delimiter
        {
            get { return _delimiter; }
            set { _delimiter = value; }
        }

        // split on ",", but not on "\,"... tricky problem. Regexps and split don't cut it, so do it the oldfashioned way. 
        private string[] DN2Components(string sDN)
        {
            StringBuilder s = new StringBuilder();
            List<string> arrayRDN = new List<string>();
            int i;

            for (i = 0; i < sDN.Length; i++)
            {
                // we have a new RDN if we have "," but the previous char was not "\"
                if (sDN[i] == ',' && sDN[i - 1] != '\\')
                {
                    arrayRDN.Add(s.ToString());
                    s.Length = 0;
                }
                else
                {
                    s.Append(_dn[i]);
                }
            }
            arrayRDN.Add(s.ToString());

            return arrayRDN.ToArray();
        }

        public string[] Components()
        {
            return _components;
        }

        public string[] ReversedComponents()
        {
            List<string> reverseList = new List<string>();
            
            for (int i = _components.Length - 1; i >= 0; i--)
            {
                reverseList.Add(_components[i]);
            }
            return reverseList.ToArray();
        }

        // return all DN components as a CSV string. 
        public string csvDN(string delimiter)
        {
            StringBuilder s = new StringBuilder();
            int i;

            for (i=0; i<_components.Length-1; i++)
            {
                s.Append("\"" + _components[i] + "\"" + delimiter);
            }
            s.Append("\"" + _components[i] + "\"");
            
            return s.ToString();
        }

        public string csvDN()
        {
            return csvDN(_delimiter);
        }


        // prepend empty RDNs, such that the total number of RDN's is equal to Count. 
        public string PadLeftcsvDN(int count, string delimiter)
        {
            StringBuilder s = new StringBuilder();
            int i, paddingCount;

            paddingCount = count - _components.Length;
            for (i = 0; i < paddingCount; i++)
            {
                s.Append("\"\"" + delimiter);
            }
            s.Append(csvDN(delimiter));

            return s.ToString();
        }

        public string PadLeftcsvDN(string delimiter)
        {
            return PadLeftcsvDN(_padLength, delimiter);
        }

        public string PadLeftcsvDN(int count)
        {
            return PadLeftcsvDN(count, _delimiter);
        }

        public string PadLeftcsvDN()
        {
            return PadLeftcsvDN(_padLength, _delimiter);
        }

        // quick attempt to extract the domain name from a DN. 
        // searching for a DC= in the DN and then taking the rest will fail for DNS app partitions:
        // dc=domain.local,cn=microsoftdns,dc=domaindnszones,dc=domain,dc=local. 
        public string DomainName
        {
            get
            {
                StringBuilder s = new StringBuilder(null);

                for (int i = _components.Length - 1; i >= 0; i--)
                {
                    if (_components[i].ToLowerInvariant().StartsWith("dc="))
                    {
                        s.Insert(0, _components[i].Substring("dc=".Length) + ".");
                    }
                    else
                    {
                        break;
                    }
                }
                s.Length--;
                return s.ToString();
            }
        }


        public int Count
        {
            get
            {
                return _components.Length;
            }
        }
    }

    public class DNCollection : IEnumerable
    {
        private List<DistinguishedName> _DNList = new List<DistinguishedName>();
        private int _largestNumberRDNs = 0;
        
        public void Add(string sDN)
        {
            Add (new DistinguishedName(sDN));
        }

        public void Add(DistinguishedName dn)
        {
            _DNList.Add(dn);
            if (dn.Count > _largestNumberRDNs)
            {
                _largestNumberRDNs = dn.Count;
            }
        }

        public void Clear()
        {
            _DNList.Clear();
            _largestNumberRDNs = 0;
        }

        public int Count
        {
            get
            {
                return _DNList.Count;
            }
        }

        public int LargestNumberRDNs
        {
            get
            {
                return _largestNumberRDNs;
            }
        }

        // Use this after you finish filling the collection. It sets some parameters on all members. 
        public void SetMetrics(int largestNumberRDNs)
        {
            foreach (DistinguishedName dn in _DNList)
            {
                dn.padLength = largestNumberRDNs;
            }
        }

        public void SetMetrics()
        {
            SetMetrics(_largestNumberRDNs);
        }
        
        #region IEnumerable, IEnumerator

        public IEnumerator GetEnumerator()
        {
            return new DNEnumerator(this);
        }

        private class DNEnumerator : IEnumerator
        {
            private DNCollection _collection;
            private int _current;

            public DNEnumerator(DNCollection collection)
            {
                this._collection = collection;
                Reset();
            }

            public object Current
            {
                get
                {
                    return _collection._DNList[_current];
                }
            }

            public bool MoveNext()
            {
                _current++;
                return _current < _collection._DNList.Count;
            }

            public void Reset()
            {
                _current = -1;
            }
        }

        #endregion
    }
}
